--TFF: Tiny F...ast Felth. Author: notabug.org/debris/ . Fundamental macros have been added.
--Lua/LuaJIT/Luau/(Untested) Computercraft/OpenComputers- compatible.
--This file is licensed under the Lesser GNU General Public License version 3 or later.
local tableremove,tableinsert,tablemove,gmatch,unpack = table.remove,table.insert,table.move,string.gmatch,(unpack or table.unpack)
local macros = {
	["LOG#REAR"]=function(front,rear,env)
		print(unpack(rear)) end,
	["!"] = function(front,rear,env) --Arity-conscious apply:
		--Given rear [.. F Z] , pops F, Z, Z elements in .. , and pushes all in F(..) to rear. 
		local Z,F = tableremove(rear),tableremove(rear)
		local ARGS = {}
		for I = Z,1,-1 do
			ARGS[I] = tableremove(rear) end
		local R = {F(unpack(ARGS))}
		tablemove(R,1,#R,#rear+1,rear) end,
	["#="] = function(front,rear,env) --Define variable:
		--Define a variable of name (pop front) with value (pop rear).
		env[tableremove(front)]=tableremove(rear) end,
	["DEFM#"] = function(front,rear,env) --Define macro without arguments:
		--[DEFM# SYMBOLNAME SYMBOLEND .. SYMBOLEND]
		--will define a macro SYMBOLNAME that when called will result in .. being pushed to rear.
		local N,E = tableremove(front),tableremove(front) 
		local R = "" --Macro source.
		local C = tableremove(front)
		while C and C~=E do
			R = R .. C .. " "
			C=tableremove(front) end
		env.macros[N] = function(front2,rear2,env2) 
			env2.feval(R,env2,nil,rear2) end end,
	["IF#"] = function(front,rear,env) --Conditional:
		--[predicate IF# SYMBOLEND .thenclause. SYMBOLEND .otherwiseclause. SYMBOLEND]
		local E,C = tableremove(front),tableremove(front)
		local T,O,S = {},{},{}
		local P = tableremove(rear) --predicate
		if not P then --else offset:
			repeat C = tableremove(front) until C==E
			--remove then clause
			C=tableremove(front) end
		while C and C~=E do
			S[#S+1]=C
			C=tableremove(front) end
		for I = #S,1,-1 do
			T[#T+1]=S[I] end
		S=nil
		env.feval("",env,T,rear)
		if P then repeat C = tableremove(front) until C==E end
		--remove else clause
		end,}

local stdenv = _ENV or getfenv(1) --TODO: Platform compatibility assignment through _VERSION.
--Arithmetic.
stdenv["true"] = true
stdenv["false"] = false
--nil=nil by default
function swap2(a,b) return b,a end
function swap3(a,b,c) return c,a,b end
function discard() end
function eq(a,b) return a==b end
function sum(s,v,...) if (v) then return sum(s+v, ...) else return s end end
function dif(s,v,...) if (v) then return dif(s-v, ...) else return s end end
function mul(s,v,...) if (v) then return mul(s*v, ...) else return s end end
function divraw(s,v,...) if (v) then return s/mul(v, ...) else return s end end
function div(s,v,...) if (v) then return s/mul(v, ...) else return 1/s end end
function dup(v) return v,v end

stdenv["+"],stdenv["-"],stdenv["*"],stdenv["/"],stdenv["div"],stdenv["macros"] = sum,dif,mul,div,divraw, macros
stdenv[":"] = dup
stdenv["=?"] = eq
stdenv["swap2"],stdenv["swap3"] = swap2,swap3
stdenv["discard"] = discard
--The meat of this file.
local function feval(str,env,front,rear)
	local env,parsed,tokens,n,rear,front = env or stdenv or {macros}, gmatch(str,'[^ ]+'),{},1,rear or {},front or {}
	local macros = env.macros
	for token in parsed do
		tokens[n]=token
	        n = n+1 end
	parsed = nil
	for tokeni = #front+1,#front + #tokens do
		front[tokeni] = tableremove(tokens) end --Reverse.
	tokens = nil
	for tokeni = 1,#front do
		local token = tableremove(front)
		local m = macros[token]
		if m==nil then
			local v = env[token]
			if v==nil then v=tonumber(token) end
			rear[#rear+1] = v
		else m(front,rear,env) end end 
	return rear end
local function repl(inputmethod,outputmethod,env)
	repeat local s = inputmethod()
		local res,ret = pcall(feval,s,env)
		outputmethod((res and "=< " or "!< ") , ret) until s == "\\q" end
stdenv.feval = feval
return {eval=feval,repl=repl,macros=macros,stdenv=stdenv}
